package com.arron.juc;

/**
 * @Classname ABADemo
 * @Description TODO
 * @Date 2020/8/20 22:47
 * @Created by 刘
 */

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 *  如何解决ABA问题？有猫腻
 *  使用AtomicStampedReference类似乐观锁进行控制
 */
public class ABADemo {

    static AtomicInteger atomicInteger = new AtomicInteger(100);
    //初始化值为100 并且给定版本号为1
    static AtomicStampedReference<Integer> atomicStampedReference =new AtomicStampedReference<>(100,1);


    public static void main(String[] args) {

        System.out.println("========以下是产生的ABA问题==========");
        new Thread(()->{
            atomicInteger.compareAndSet(100,101);
            atomicInteger.compareAndSet(101,100);
        },"A").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = atomicInteger.compareAndSet(100, 101);
            System.out.println(Thread.currentThread().getName()+"\t修改结果："+b);
        },"B").start();

       while (Thread.activeCount()>2){
           //等待前面2个线程执行完毕
       }
        System.out.println("========以下是解决的ABA问题==========");

        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"拿到版本号："+stamp);
            //休眠一会儿保证线程D和自己拿到同一个版本号
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //进行猫腻操作
            atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);
            atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
        },"C").start();
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"拿到版本号："+stamp);
            //休眠一会儿，等待线程C操作完毕
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改值
            boolean andSet = atomicStampedReference.compareAndSet(100, 2020, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"\t修改结果："+andSet);
            //结果修改失败，是因为版本号已变

        },"D").start();

    }




}
